﻿// XmlVisualizer
// Copyright (C) 2009  Roman Betík - rbetik@gmail.com

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

using System;
using System.Configuration;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.Xml.Linq;
using System.Linq;
using System.Xml.Schema;
using System.IO;
using System.Xml.XPath;
using System.Collections;
using System.Collections.ObjectModel;
using System.Threading;

using XmlVisualizer.Models;
using XmlVisualizer.Controllers;
using XmlVisualizer.DiagramDesigner;
using XmlVisualizer.DiagramDesigner.Controls;
using XmlVisualizer.Controls;
using Tomers.WPF.Localization;
using System.Collections.Generic;

namespace XmlVisualizer {
	public partial class Document : Window {
		#region Model event handlers

		void appModel_Shutdown(object sender, EventArgs e) {
			Dispatcher.Invoke((Action)(() => {
				MessageBox.Show("Application is shutting down due to an unexpected error.");
			}
			));
		}

		/// <summary>
		/// Shows the loaded file.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_Show(object sender, DisplayXmlEventArgs e) {
			Draw(e.Nodes, e.ClearCanvas, e.ConnectWithAlreadyPlaced);
		}

		/// <summary>
		/// Invoked when document gets saved.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentSaved(object sender, EventArgs e) {
			Modified = false;
		}

		/// <summary>
		/// Invoked when the document gets modified by the user.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentChanged(object sender, DocumentChangedEventArgs e) {
			Modified = true;

			if (e.ChangeType == XmlNodeType.Element) {
				if (displayedNodes.ContainsKey(e.NewValue as XNode)) {
					XNodeInfo info = displayedNodes[e.NewValue as XNode];
					Control ui = info.UI;
					if (ui is DesignerItem) {
						DesignerItem designerItem = ui as DesignerItem;
						if (designerItem.Content is ucElement) {
							ucElement element = designerItem.Content as ucElement;

							XElement el = e.NewValue as XElement;
							element.ElementName = el.Name.LocalName;
							element.ElementNamespace = el.Name.NamespaceName;
							ucElement.FillObservableCollection<XAttribute>(element.ObservableAttributes, el.Attributes());
						}
					}
				}
			} else if (e.ChangeType == XmlNodeType.Attribute) {
				if (displayedNodes.ContainsKey(e.NewValue as XNode)) {
					XNodeInfo info = displayedNodes[e.NewValue as XNode];
					Control ui = info.UI;
					if (ui is DesignerItem) {
						DesignerItem designerItem = ui as DesignerItem;
						if (designerItem.Content is ucElement) {
							ucElement element = designerItem.Content as ucElement;

							XElement el = e.NewValue as XElement;
							//element.ElementName = el.Name.LocalName;
							//element.ElementNamespace = el.Name.NamespaceName;
							ucElement.FillObservableCollection<XAttribute>(element.ObservableAttributes, el.Attributes());
						}
					}
				}
			}

			UpdateSourceText();
		}

		/// <summary>
		/// Handles loading execution.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentLoading(object sender, EventArgs e) {
			if (statusTextLoading.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
				XmlLoadingHandler();
			} else {
				statusTextLoading.Dispatcher.BeginInvoke((Action)(() => {
					XmlLoadingHandler();
				}));
			}
		}

		/// <summary>
		/// Invoked when the document gets loaded.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentLoaded(object sender, EventArgs e) {
			if (statusTextLoaded.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
				XmlLoadCompletedHandler();
			} else {
				this.Dispatcher.Invoke((Action)(() => {
					XmlLoadCompletedHandler();
				}));
			}
		}

		/// <summary>
		/// Invoked when the document gets closed.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentClosed(object sender, EventArgs e) {
			ClearCanvas();
			ClearSourceText();
			Reset();
		}

		/// <summary>
		/// Invoked when the document's wellformedness changes.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_DocumentWellformednessChanged(object sender, WellformednessEventArgs e) {
			if (e.Wellformed) {
				this.Dispatcher.Invoke((Action)(() => {
					this.wellformed = e.Wellformed;
					statusWellformed.Visibility = Visibility.Visible;
					statusNotWellformed.Visibility = Visibility.Collapsed;
				}
				));
			} else {
				this.Dispatcher.Invoke((Action)(() => {
					this.wellformed = e.Wellformed;
					statusWellformed.Visibility = Visibility.Collapsed;
					statusNotWellformed.Visibility = Visibility.Visible;
				}
				));
			}
		}

		/// <summary>
		/// Invoked when some node's XNode representation changes.
		/// </summary>
		/// <param name="sender">The object where the event handler is attached.</param>
		/// <param name="e">The event data.</param>
		void appModel_NodeChanged(object sender, NodeChangedEventArgs e) {
			if (displayedNodes.ContainsKey(e.OldNode)) {
				XNodeInfo oldNodeInfo = displayedNodes[e.OldNode];
				oldNodeInfo.Node = e.NewNode;

				//if (oldNodeInfo.UI == null)
				//    throw new ArgumentException("oldNodeInfo.UI is null");

				//// create new XNodeInfo
				//XNodeInfo newNodeInfo = new XNodeInfo(oldNodeInfo.Position, oldNodeInfo.Depth, oldNodeInfo.Order, oldNodeInfo.AppearanceTemplate, e.NewNode);
				//newNodeInfo.UI = oldNodeInfo.UI;

				// update reference to XNode/XElement in UI item
				if (oldNodeInfo.UI is DesignerItem) {
					DesignerItem designerItem = oldNodeInfo.UI as DesignerItem;
					designerItem.Node = e.NewNode;
					if (e.NewNode is XElement) {
						designerItem.Element = e.NewNode as XElement;

						object content = designerItem.Content;
						if (content is ucElement) {
							ucElement con = content as ucElement;
							con.Element = e.NewNode as XElement;
						}
					} else {
						object content = designerItem.Content;
						if (content is ucTextNode) {
							ucTextNode con = content as ucTextNode;
							con.Node = e.NewNode;
						}
					}
				}

				// update displayedNodes collection
				displayedNodes.Remove(e.OldNode);
				displayedNodes.Add(e.NewNode, oldNodeInfo);
			}
		}

		void appModel_SelectionChanged(object sender, XmlVisualizer.Models.SelectionChangedEventArgs e) {
			IEnumerable<XObject> objects = e.Objects;
			designerCanvas.selectionService.ClearSelection();

			statusXPathNodes.Visibility = Visibility.Visible;
			statusXPath.Visibility = Visibility.Visible;

			statusXPathNodes.Text = objects.Count().ToString();

			foreach (XObject obj in objects) {
				SelectObject(obj);
			}
		}

		void appModel_ModeChanged() {
			Mode mode = Model.GetMode();
			statusModeMode.Text = mode.ToString();
		}

		void appModel_SaveXmv(object sender, XmvEventArgs e) {
			designerCanvas.SaveXmv(e.Filename);
		}

		void appModel_OpenXmv(object sender, XmvEventArgs e) {
			designerCanvas.OpenXmv(e.Filename);
		}

		void appModel_DocumentValidityChanged(object sender, ValidityEventArgs e) {
			this.Dispatcher.Invoke((Action)(() => {
				if (e.Valid) {
					statusValid.Visibility = Visibility.Visible;
					statusNotValid.Visibility = Visibility.Collapsed;
				} else {
					statusNotValid.Visibility = Visibility.Visible;
					statusValid.Visibility = Visibility.Collapsed;
				}
			}));
		}

		private void SelectObject(XObject obj) {
			if (obj is XNode) {
				XNode node = obj as XNode;
				if (displayedNodes.ContainsKey(node)) {
					XNodeInfo info = displayedNodes[node];
					designerCanvas.selectionService.AddToSelection(info.UI as DesignerItem);
					if (obj is XElement) {
						XElement element = obj as XElement;
						foreach (XNode n in element.Nodes()) {
							SelectObject(n);
						}
					}
				}
			} else if (obj is XAttribute) {
				XAttribute att = obj as XAttribute;
				XElement parentElement = att.Parent;
				if (parentElement != null) {
					if (displayedNodes.ContainsKey(parentElement)) {
						XNodeInfo info = displayedNodes[parentElement];
						designerCanvas.selectionService.AddToSelection(info.UI as DesignerItem);
					}
				}
			}
		}

		/// <summary>
		/// Handler showing load progress.
		/// </summary>
		private void XmlLoadingHandler() {
			Dispatcher.Invoke((Action)(() => {
				statusTextLoaded.Visibility = Visibility.Collapsed;
				statusTextLoading.Visibility = Visibility.Visible;
			}
			));
		}

		/// <summary>
		/// Updates status text to show the file has been loaded.
		/// </summary>
		private void XmlLoadCompletedHandler() {
			Dispatcher.Invoke((Action)(() => {
				statusTextLoaded.Visibility = Visibility.Visible;
				statusTextLoading.Visibility = Visibility.Collapsed;
			}
			));
		}

		/// <summary>
		/// Draws the document.
		/// </summary>
		private void Draw(XNodeCollection nodes, bool clearCanvas, bool connectWithAlreadyPlaced) {
			switch (this.appModel.Mode) {
				case Mode.Xml:
				case Mode.BigXml:
					DrawXml(nodes, clearCanvas, connectWithAlreadyPlaced);
					break;
				default:
					break;
			}

			UpdateSourceText();
		}

		public void UpdateSourceText() {
			Dispatcher.Invoke((Action)(() => {
				xmlcode.Clear();
				String text = String.Empty;
				text = appModel.GetXmlText();
				xmlcode.Text = text;
			}
			));
		}
		#endregion
	}
}